/**
 @file sys_usw_phy.c

 @author  Copyright (C) 2021 Centec Networks Inc.  All rights reserved.

 @date 2021-11-19

 @version v2.0

*/

/****************************************************************************
 *
* Header Files
*
****************************************************************************/
#include "sal.h"
#include "dal.h"
#include "ctc_error.h"
#include "ctc_debug.h"
#include "ctc_warmboot.h"

#include "sys_usw_common.h"
#include "sys_usw_dmps_db.h"
#include "sys_usw_port.h"
#include "sys_usw_phy.h"
#include "sys_usw_peri.h"
#include "sys_usw_wb_common.h"

#include "drv_api.h"

/****************************************************************************
 *
* Defines and Macros
*
*****************************************************************************/
#define PHY_PORT_SHIM_INFO(lchip, lport) p_usw_phy_master[lchip]->phy_port_info[lport].p_phy_shim
#define PHY_PORT_SHIM_DRV(lchip, lport) p_usw_phy_master[lchip]->phy_port_info[lport].p_phy_shim->driver
#define PHY_PORT_INIT(lchip, lport) p_usw_phy_master[lchip]->phy_port_info[lport].is_init
#define PHY_PORT_LINK(lchip, lport) p_usw_phy_master[lchip]->phy_port_info[lport].link_status

struct sys_phy_shim_s
{
    ctc_slistnode_t head;
    ctc_chip_phy_shim_t phy_shim;
};
typedef struct sys_phy_shim_s sys_phy_shim_t;


struct sys_phy_port_info_s
{
    ctc_chip_phy_shim_t* p_phy_shim;
    uint8 is_init :1;
    uint8 link_status:1;
    uint8 rsv :6;
};
typedef struct sys_phy_port_info_s sys_phy_port_info_t;

struct sys_phy_master_s
{
    sys_phy_port_info_t*   phy_port_info;
    ctc_slist_t* phy_list;
    uint8   *port_mdio_mapping_tbl;
    uint8   *port_phy_mapping_tbl;
    uint8   *port_dev_no_mapping_tbl;
};
typedef struct sys_phy_master_s sys_phy_master_t;
/****************************************************************************
 *
* Global and Declaration
*
*****************************************************************************/


sys_phy_master_t* p_usw_phy_master[CTC_MAX_LOCAL_CHIP_NUM_PP] = {NULL};
/****************************************************************************
 *
* Function
*
*****************************************************************************/

int32
sys_usw_phy_get_phy_id(uint8 lchip, uint16 lport, uint32* phy_id)
{
    uint32 gport = 0;
    uint8  gchip = 0;
    CTC_ERROR_RETURN(sys_usw_get_gchip_id(lchip, &gchip));
    gport = SYS_MAP_DRV_LPORT_TO_CTC_GPORT(gchip, lport);
    SYS_MAX_PHY_PORT_CHECK(gport);

    if (lport >= SYS_PHY_PORT_NUM_PER_SLICE)
    {
        return CTC_E_INVALID_CONFIG;
    }

    if (NULL != PHY_PORT_SHIM_INFO(lchip, lport))
    {
        *phy_id = (PHY_PORT_SHIM_INFO(lchip, lport)->phy_id & PHY_PORT_SHIM_INFO(lchip, lport)->phy_id_mask);
    }
    else
    {
        return CTC_E_NOT_INIT;
    }

    return CTC_E_NONE;

}

int32
sys_usw_phy_get_phy_register_exist(uint8 lchip, uint16 lport)
{
    uint32 gport = 0;
    uint8  gchip = 0;

    CTC_ERROR_RETURN(sys_usw_get_gchip_id(lchip, &gchip));
    gport = SYS_MAP_DRV_LPORT_TO_CTC_GPORT(gchip, lport);
    SYS_MAX_PHY_PORT_CHECK(gport);


    if (NULL != PHY_PORT_SHIM_INFO(lchip, lport))
    {
        return CTC_E_NONE;
    }

    return CTC_E_NOT_INIT;
}


int32
sys_usw_phy_set_phy_mapping(uint8 lchip, ctc_chip_phy_mapping_para_t* phy_mapping_para)
{
    SYS_PHY_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);

    sal_memcpy(p_usw_phy_master[lchip]->port_mdio_mapping_tbl, phy_mapping_para->port_mdio_mapping_tbl,
               SYS_PHY_PORT_NUM_PER_SLICE);
    
    sal_memcpy(p_usw_phy_master[lchip]->port_phy_mapping_tbl, phy_mapping_para->port_phy_mapping_tbl,
               SYS_PHY_PORT_NUM_PER_SLICE);

    if(NULL != MCHIP_PERI(lchip)->peri_set_phy_scan_cfg)
    {
        CTC_ERROR_RETURN(MCHIP_PERI(lchip)->peri_set_phy_scan_cfg(lchip));
    }

    return CTC_E_NONE;
}

/**
 @brief get phy to port mapping
*/
int32
sys_usw_phy_get_phy_mapping(uint8 lchip, ctc_chip_phy_mapping_para_t* p_phy_mapping_para)
{
    SYS_PHY_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);

    sal_memcpy(p_phy_mapping_para->port_mdio_mapping_tbl,  p_usw_phy_master[lchip]->port_mdio_mapping_tbl,
               SYS_PHY_PORT_NUM_PER_SLICE);
    
    sal_memcpy(p_phy_mapping_para->port_phy_mapping_tbl,  p_usw_phy_master[lchip]->port_phy_mapping_tbl,
                   SYS_PHY_PORT_NUM_PER_SLICE);

    return CTC_E_NONE;
}

STATIC  int32
_sys_usw_phy_driver_probe(uint8 lchip, uint16 lport, uint8 type)
{
    uint8 found = 0;
    uint32 phy_id;
    ctc_chip_mdio_para_t para;
    ctc_slistnode_t        *node = NULL;
    sys_phy_shim_t* p_phy_shim = NULL;

    sal_memset(&para, 0, sizeof(ctc_chip_mdio_para_t));

    para.bus = p_usw_phy_master[lchip]->port_mdio_mapping_tbl[lport];
    para.phy_addr = p_usw_phy_master[lchip]->port_phy_mapping_tbl[lport];
    para.dev_no = p_usw_phy_master[lchip]->port_dev_no_mapping_tbl[lport];
    
    if (para.bus == 0xFF)
    {
        phy_id = CTC_CHIP_PHY_NULL_PHY_ID;
    }
    else
    {
        /*regiser 2 high 16bits, register 3 low 16bits*/
        para.reg = 2;
        CTC_ERROR_RETURN(sys_usw_peri_mdio_read(lchip, type, &para));
        phy_id = para.value<<16;
        para.reg = 3;
        CTC_ERROR_RETURN(sys_usw_peri_mdio_read(lchip, type, &para));
        phy_id = phy_id|para.value;
    }

    CTC_SLIST_LOOP(p_usw_phy_master[lchip]->phy_list, node)
    {
        p_phy_shim = _ctc_container_of(node, sys_phy_shim_t, head);
        if ((p_phy_shim->phy_shim.phy_id&p_phy_shim->phy_shim.phy_id_mask) == (phy_id&p_phy_shim->phy_shim.phy_id_mask))
        {
            found = 1;
            break;
        }
    }

    if (1 == found)
    {
        PHY_PORT_SHIM_INFO(lchip, lport) = (ctc_chip_phy_shim_t*)&(p_phy_shim->phy_shim);
        return CTC_E_NONE;
    }

    return CTC_E_NOT_EXIST;
}

STATIC int32
_sys_usw_phy_set_phy_prop(uint8 lchip, uint16 lport, uint16 phy_prop_type, void* p_value)
{
    ctc_chip_mdio_type_t type = CTC_CHIP_MDIO_GE;
    ctc_chip_mdio_para_t para;
    bool phy_scan_status = FALSE;
    int32 ret = CTC_E_NONE;
    uint32 tmp_val32[SYS_CHIP_CONTROL_NUM] = {0};
    uint32 mdio_scan_bmp[SYS_CHIP_MAX_MDIO_BUS][SYS_CHIP_CONTROL_NUM] = {{0}, {0}, {0}, {0}};
    uint8 ctl_id = 0;
    uint32 port_type = 0;
    uint32 speed_mode = 0;
    sys_dmps_port_info_t port_info = {0};
    
    sal_memset(&para, 0, sizeof(ctc_chip_mdio_para_t));
    port_info.lport = lport;

    CTC_ERROR_RETURN(sys_usw_dmps_db_api_get_port_property(lchip, &port_info, SYS_PORT_API_DMPS_PROP_PORT_TYPE, (void *)&port_type));
    CTC_ERROR_RETURN(sys_usw_dmps_db_api_get_port_property(lchip, &port_info, SYS_PORT_API_DMPS_PROP_SPEED_MODE, (void *)&speed_mode));

    if ((port_type != SYS_DMPS_NETWORK_PORT) && (port_type != SYS_DMPS_CPUMAC_NETWORK_PORT))
    {
        SYS_PHY_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " set phy driver lport %d is not used \n", lport);
        return CTC_E_INVALID_LOCAL_PORT;
    }
    if (CTC_PORT_SPEED_10G == speed_mode)
    {
        type = CTC_CHIP_MDIO_XG;
    }
    para.bus = p_usw_phy_master[lchip]->port_mdio_mapping_tbl[lport];
    para.phy_addr = p_usw_phy_master[lchip]->port_phy_mapping_tbl[lport];
    para.dev_no = p_usw_phy_master[lchip]->port_dev_no_mapping_tbl[lport];

    CTC_ERROR_RETURN(sys_usw_peri_get_phy_scan_en(lchip, &phy_scan_status));
    if (phy_scan_status)
    {
        for (ctl_id = 0; ctl_id < MCHIP_CAP(SYS_CAP_MDIO_CTL_NUM); ctl_id++)
        {
            CTC_ERROR_RETURN(sys_usw_peri_get_phy_bitmap(lchip, ctl_id, &tmp_val32[0], &tmp_val32[1]));
            mdio_scan_bmp[ctl_id][0] = tmp_val32[0];
            mdio_scan_bmp[ctl_id][1] = tmp_val32[1];
            CTC_ERROR_RETURN(sys_usw_peri_set_phy_bitmap(lchip, ctl_id, 0, 0));
        }
    }
    if (NULL == PHY_PORT_SHIM_INFO(lchip, lport))
    {
        CTC_ERROR_GOTO(_sys_usw_phy_driver_probe(lchip, lport, type), ret, rollback);
    }
    switch(phy_prop_type)
    {
        case CTC_PORT_PROP_PHY_INIT:
            {
                uint32 value = 0;
                value = *((uint32*)p_value);
                if (PHY_PORT_SHIM_DRV(lchip, lport).init && value)
                {
                    if (0 == PHY_PORT_INIT(lchip, lport))
                    {
                        CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).init(CTC_MAP_LCHIP(lchip), &para), ret, rollback);
                        PHY_PORT_INIT(lchip, lport) = 1;
                    }
                }
                else if ((PHY_PORT_SHIM_DRV(lchip, lport).deinit) && (0 == value))
                {
                    CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).deinit(CTC_MAP_LCHIP(lchip), &para), ret, rollback);
                    PHY_PORT_INIT(lchip, lport) = 0;
                    PHY_PORT_SHIM_INFO(lchip, lport) = NULL;
                }
                else
                {
                    return CTC_E_INVALID_PARAM;
                }
            }
            break;
        case CTC_PORT_PROP_PHY_EN:
            if (PHY_PORT_SHIM_DRV(lchip, lport).set_port_en)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).set_port_en(CTC_MAP_LCHIP(lchip), &para, *((uint32*)p_value)), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_PHY_DUPLEX:
            if (PHY_PORT_SHIM_DRV(lchip, lport).set_port_duplex_en)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).set_port_duplex_en(CTC_MAP_LCHIP(lchip), &para, *((uint32*)p_value)), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_PHY_MEDIUM:
            if (PHY_PORT_SHIM_DRV(lchip, lport).set_port_medium)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).set_port_medium(CTC_MAP_LCHIP(lchip), &para, *((uint32*)p_value)), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_PHY_LOOPBACK:
            if (PHY_PORT_SHIM_DRV(lchip, lport).set_port_loopback)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).set_port_loopback(CTC_MAP_LCHIP(lchip), &para, *((uint32*)p_value)), ret, rollback);
            }
            break;

        case CTC_PORT_PROP_PHY_AUTO_NEG_EN:
            if (PHY_PORT_SHIM_DRV(lchip, lport).set_port_auto_neg_en)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).set_port_auto_neg_en(CTC_MAP_LCHIP(lchip), &para, *((uint32*)p_value)), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_PHY_SPEED:
            if (PHY_PORT_SHIM_DRV(lchip, lport).set_port_speed)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).set_port_speed(CTC_MAP_LCHIP(lchip), &para, *((uint32*)p_value)), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_UNIDIR_EN:
            if (PHY_PORT_SHIM_DRV(lchip, lport).set_port_unidir_en)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).set_port_unidir_en(CTC_MAP_LCHIP(lchip), &para, *((uint32*)p_value)), ret, rollback);
            }
            break;
        default:
            if ((phy_prop_type >= CTC_PORT_PROP_PHY_CUSTOM_BASE) && (phy_prop_type <= CTC_PORT_PROP_PHY_CUSTOM_MAX_TYPE))
            {
                if (PHY_PORT_SHIM_DRV(lchip, lport).set_ext_attr)
                {
                    CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).set_ext_attr(CTC_MAP_LCHIP(lchip), &para, phy_prop_type, p_value), ret, rollback);
                }
            }
            else
            {
                return CTC_E_INVALID_PARAM;
            }

    }
    if (phy_scan_status)
    {
        for (ctl_id = 0; ctl_id < MCHIP_CAP(SYS_CAP_MDIO_CTL_NUM); ctl_id++)
        {
            CTC_ERROR_RETURN(sys_usw_peri_set_phy_bitmap(lchip, ctl_id, mdio_scan_bmp[ctl_id][0], mdio_scan_bmp[ctl_id][1]));
        }
    }

    return CTC_E_NONE;

rollback:
    if (phy_scan_status)
    {
        for (ctl_id = 0; ctl_id < MCHIP_CAP(SYS_CAP_MDIO_CTL_NUM); ctl_id++)
        {
            sys_usw_peri_set_phy_bitmap(lchip, ctl_id, mdio_scan_bmp[ctl_id][0], mdio_scan_bmp[ctl_id][1]);
        }
    }

    return ret;
}

STATIC int32
_sys_usw_phy_get_phy_prop(uint8 lchip, uint16 lport, uint16 phy_prop_type, void* p_value)
{
    ctc_chip_mdio_para_t para;
    bool phy_scan_status = FALSE;
    int32 ret = CTC_E_NONE;
    uint32 tmp_val32[SYS_CHIP_CONTROL_NUM] = {0};
    uint32 mdio_scan_bmp[SYS_CHIP_MAX_MDIO_BUS][SYS_CHIP_CONTROL_NUM] = {{0}, {0}, {0}, {0}};
    uint8 ctl_id = 0;
    uint32 port_type = 0;
    sys_dmps_port_info_t port_info = {0};

    sal_memset(&para, 0, sizeof(ctc_chip_mdio_para_t));
    port_info.lport = lport;

    CTC_ERROR_RETURN(sys_usw_dmps_db_api_get_port_property(lchip, &port_info, SYS_PORT_API_DMPS_PROP_PORT_TYPE, (void *)&port_type));
    if ((port_type != SYS_DMPS_NETWORK_PORT) && (port_type != SYS_DMPS_CPUMAC_NETWORK_PORT))
    {
        SYS_PHY_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " get phy driver lport %d is not used \n", lport);
        if ((CTC_PORT_PROP_LINK_UP == phy_prop_type) && (NULL != p_value))
        {
            *(uint32*)p_value = 0;
        }
        return CTC_E_INVALID_LOCAL_PORT;
    }

    if (NULL == PHY_PORT_SHIM_INFO(lchip, lport))
    {
        return CTC_E_NOT_INIT;
    }

    para.bus = p_usw_phy_master[lchip]->port_mdio_mapping_tbl[lport];
    para.phy_addr = p_usw_phy_master[lchip]->port_phy_mapping_tbl[lport];
    para.dev_no = p_usw_phy_master[lchip]->port_dev_no_mapping_tbl[lport];

    CTC_ERROR_RETURN(sys_usw_peri_get_phy_scan_en(lchip, &phy_scan_status));
    if (phy_scan_status)
    {
        for (ctl_id = 0; ctl_id < MCHIP_CAP(SYS_CAP_MDIO_CTL_NUM); ctl_id++)
        {
            CTC_ERROR_RETURN(sys_usw_peri_get_phy_bitmap(lchip, ctl_id, &tmp_val32[0], &tmp_val32[1]));
            mdio_scan_bmp[ctl_id][0] = tmp_val32[0];
            mdio_scan_bmp[ctl_id][1] = tmp_val32[1];
            CTC_ERROR_RETURN(sys_usw_peri_set_phy_bitmap(lchip, ctl_id, 0, 0));
        }
    }
    switch(phy_prop_type)
    {
        case CTC_PORT_PROP_PHY_EN:
            if (PHY_PORT_SHIM_DRV(lchip, lport).get_port_en)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).get_port_en(CTC_MAP_LCHIP(lchip), &para, (uint32*)p_value), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_PHY_DUPLEX:
            if (PHY_PORT_SHIM_DRV(lchip, lport).get_port_duplex_en)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).get_port_duplex_en(CTC_MAP_LCHIP(lchip), &para, (uint32*)p_value), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_PHY_MEDIUM:
            if (PHY_PORT_SHIM_DRV(lchip, lport).get_port_medium)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).get_port_medium(CTC_MAP_LCHIP(lchip), &para, (uint32*)p_value), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_PHY_LOOPBACK:
            if (PHY_PORT_SHIM_DRV(lchip, lport).get_port_loopback)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).get_port_loopback(CTC_MAP_LCHIP(lchip), &para, (uint32*)p_value), ret, rollback);
            }
            break;

        case CTC_PORT_PROP_PHY_AUTO_NEG_EN:
            if (PHY_PORT_SHIM_DRV(lchip, lport).get_port_auto_neg_en)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).get_port_auto_neg_en(CTC_MAP_LCHIP(lchip), &para, (uint32*)p_value), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_PHY_SPEED:
            if (PHY_PORT_SHIM_DRV(lchip, lport).get_port_speed)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).get_port_speed(CTC_MAP_LCHIP(lchip), &para, (uint32*)p_value), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_LINK_UP:
            if (PHY_PORT_SHIM_DRV(lchip, lport).get_link_up_status)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).get_link_up_status(CTC_MAP_LCHIP(lchip), &para, (uint32*)p_value), ret, rollback);
            }
            break;
        case CTC_PORT_PROP_UNIDIR_EN:
            if (PHY_PORT_SHIM_DRV(lchip, lport).get_port_unidir_en)
            {
                CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).get_port_unidir_en(CTC_MAP_LCHIP(lchip), &para, (uint32*)p_value), ret, rollback);
            }
            break;
        default:
            if ((phy_prop_type >= CTC_PORT_PROP_PHY_CUSTOM_BASE) && (phy_prop_type <= CTC_PORT_PROP_PHY_CUSTOM_MAX_TYPE))
            {
                if (PHY_PORT_SHIM_DRV(lchip, lport).get_ext_attr)
                {
                    CTC_ERROR_GOTO(PHY_PORT_SHIM_DRV(lchip, lport).get_ext_attr(CTC_MAP_LCHIP(lchip), &para, phy_prop_type, p_value), ret, rollback);
                }
            }
            else
            {
                return CTC_E_INVALID_PARAM;
            }

    }
    if (phy_scan_status)
    {
        for (ctl_id = 0; ctl_id < MCHIP_CAP(SYS_CAP_MDIO_CTL_NUM); ctl_id++)
        {
            CTC_ERROR_RETURN(sys_usw_peri_set_phy_bitmap(lchip, ctl_id, mdio_scan_bmp[ctl_id][0], mdio_scan_bmp[ctl_id][1]));
        }
    }

    return CTC_E_NONE;

rollback:
    if (phy_scan_status)
    {
        for (ctl_id = 0; ctl_id < MCHIP_CAP(SYS_CAP_MDIO_CTL_NUM); ctl_id++)
        {
            sys_usw_peri_set_phy_bitmap(lchip, ctl_id, mdio_scan_bmp[ctl_id][0], mdio_scan_bmp[ctl_id][1]);
        }
    }

    return ret;
}

int32
sys_usw_phy_set_phy_property(uint8 lchip, uint16 lport, ctc_port_property_t port_prop, void* p_value)
{
    int32 ret = CTC_E_NONE;

    if (CTC_PORT_PROP_PHY_EN == port_prop)
    {
        uint32 phy_id = 0;
        if ((CTC_E_NONE == sys_usw_phy_get_phy_id(lchip, lport, &phy_id)) 
           && (CTC_E_NONE == sys_usw_phy_get_phy_register_exist(lchip, lport))
           && (phy_id != CTC_CHIP_PHY_NULL_PHY_ID))
        {
            ret = _sys_usw_phy_set_phy_prop(lchip, lport, CTC_PORT_PROP_PHY_EN, (void*)p_value);
        }
        else
        {
            ret = sys_usw_dmps_set_mac_property(lchip, lport, CTC_PORT_PROP_MAC_EN, *(uint32*)p_value);
        }
    }
    else
    {
        ret = _sys_usw_phy_set_phy_prop(lchip, lport, port_prop, p_value);
    }

    return ret;
}

int32
sys_usw_phy_get_phy_property(uint8 lchip, uint16 lport, ctc_port_property_t port_prop, void* p_value)
{
    int32 ret = CTC_E_NONE;

    if (CTC_PORT_PROP_PHY_EN == port_prop)
    {
       if (CTC_E_NONE == sys_usw_phy_get_phy_register_exist(lchip, lport))
        {
            ret = _sys_usw_phy_get_phy_prop(lchip, lport, CTC_PORT_PROP_PHY_EN, p_value);
        }
        else
        {
            ret = sys_usw_dmps_get_mac_property(lchip, lport, CTC_PORT_PROP_MAC_EN, (uint32*)p_value);
        }
    }
    else if (CTC_PORT_PROP_PHY_INIT == port_prop)
    {
        ret = sys_usw_phy_get_phy_register_exist(lchip, lport);
        if (CTC_E_NOT_INIT == ret)
        {
            *(uint32*)p_value = FALSE;
            ret = CTC_E_NONE;
        }
        else
        {
            *(uint32*)p_value = ret?FALSE:TRUE;
        }
    }
    else
    {
        if (CTC_E_NONE == sys_usw_phy_get_phy_register_exist(lchip, lport))
        {
            ret = _sys_usw_phy_get_phy_prop(lchip, lport, port_prop, (void*)p_value);
        }
    }

    return ret;
}

void
sys_usw_phy_link_event_polling_cb(uint8 lchip, uint32 gport)
{
    uint8 link_status_change = 0;
    uint16 lport =0;
    uint32 value = 0;
    ctc_chip_mdio_para_t para;
    int32 ret = CTC_E_NONE;
    uint32 speed_mode = 0;
    uint32 auto_neg_en = 0;
    uint32 auto_neg_mode = 0;
    sal_memset(&para, 0, sizeof(ctc_chip_mdio_para_t));

    lport = SYS_MAP_CTC_GPORT_TO_DRV_LPORT(gport);
    ret = _sys_usw_phy_get_phy_prop(lchip, lport, CTC_PORT_PROP_LINK_UP, (void*)&value);
    if (ret == CTC_E_INVALID_LOCAL_PORT || ret == CTC_E_NOT_INIT)
    {
        return;
    }
    if (NULL != PHY_PORT_SHIM_INFO(lchip, lport))
    {
        if (value != PHY_PORT_LINK(lchip, lport))
        {
            link_status_change = 1;
            SYS_PHY_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "lport %d old_link_status %d new_link_status %d\n", lport, PHY_PORT_LINK(lchip, lport), value);
        }
    }

    if (0 == link_status_change)
    {
        return;
    }

    (void)_sys_usw_phy_get_phy_prop(lchip, lport, CTC_PORT_PROP_PHY_SPEED, (void *)&speed_mode);
    (void)sys_usw_dmps_get_mac_property(lchip, lport, CTC_PORT_PROP_AUTO_NEG_EN, &auto_neg_en);
    (void)sys_usw_dmps_get_mac_property(lchip, lport, CTC_PORT_PROP_AUTO_NEG_MODE, &auto_neg_mode);

    para.bus = p_usw_phy_master[lchip]->port_mdio_mapping_tbl[lport];
    para.phy_addr = p_usw_phy_master[lchip]->port_phy_mapping_tbl[lport];
    para.dev_no = p_usw_phy_master[lchip]->port_dev_no_mapping_tbl[lport];
    PHY_PORT_LINK(lchip, lport) = value;
    CTC_ERROR_DUMP(sys_usw_dmps_set_mac_property(lchip, lport, CTC_PORT_PROP_MAC_EN, value));

    /*auto neg enable and linkup, set mac speed*/
    if (value)
    {
        if ((CTC_PORT_SPEED_1G == speed_mode) || (CTC_PORT_SPEED_100M == speed_mode) || (CTC_PORT_SPEED_10M == speed_mode))
        {
            if (auto_neg_en)
            {
                if (CTC_PORT_AUTO_NEG_MODE_SGMII_SLAVER == auto_neg_mode)
                {
                    CTC_ERROR_DUMP(sys_usw_dmps_set_mac_property(lchip, lport, CTC_PORT_PROP_SPEED, speed_mode));
                }
            }
        }
    }

    if (value)
    {
        if (PHY_PORT_SHIM_INFO(lchip, lport) && PHY_PORT_SHIM_DRV(lchip, lport).linkup_event)
        {
            PHY_PORT_SHIM_DRV(lchip, lport).linkup_event(CTC_MAP_LCHIP(lchip), &para, PHY_PORT_SHIM_INFO(lchip, lport)->user_data);
        }
    }
    else
    {
        if (PHY_PORT_SHIM_INFO(lchip, lport) && PHY_PORT_SHIM_DRV(lchip, lport).linkdown_event)
        {
            PHY_PORT_SHIM_DRV(lchip, lport).linkdown_event(CTC_MAP_LCHIP(lchip), &para, PHY_PORT_SHIM_INFO(lchip, lport)->user_data);
        }
    }
}

int32
sys_usw_phy_set_phy_register(uint8 lchip, ctc_chip_phy_shim_t* p_register)
{
    sys_phy_shim_t* p_register_tmp = NULL;

    p_register_tmp = mem_malloc(MEM_PORT_MODULE, sizeof(sys_phy_shim_t));
    if (NULL == p_register_tmp)
    {
        return CTC_E_NO_MEMORY;
    }
    sal_memset(p_register_tmp, 0, sizeof(sys_phy_shim_t));
    sal_memcpy(&(p_register_tmp->phy_shim), p_register, sizeof(ctc_chip_phy_shim_t));

    ctc_slist_add_tail(p_usw_phy_master[lchip]->phy_list, &(p_register_tmp->head));

    return CTC_E_NONE;
}



int32
sys_usw_phy_init(uint8 lchip, ctc_port_global_cfg_t* p_port_global_cfg)
{
    int32 ret = CTC_E_NONE;

    LCHIP_CHECK(lchip);
    if (NULL != p_usw_phy_master[lchip])
    {
        SYS_PHY_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " phy module have init\n");
        return CTC_E_INVALID_PARAM;
    }
    SYS_VCHIP_MASTER_INIT(lchip, p_usw_phy_master, CTC_FEATURE_DATAPATH);

    p_usw_phy_master[lchip] = (sys_phy_master_t*)mem_malloc(MEM_PORT_MODULE, sizeof(sys_phy_master_t));
    if (NULL == p_usw_phy_master[lchip])
    {
        SYS_PHY_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No memory for master \n");
        return CTC_E_NO_MEMORY;
    }
    sal_memset(p_usw_phy_master[lchip], 0, sizeof(sys_phy_master_t));

    p_usw_phy_master[lchip]->phy_list = ctc_slist_new();
    if (NULL == p_usw_phy_master[lchip]->phy_list)
    {
        ret = CTC_E_NO_MEMORY;
        goto rollback_0;
    }

    p_usw_phy_master[lchip]->phy_port_info = mem_malloc(MEM_PORT_MODULE, SYS_PHY_PORT_NUM_PER_SLICE * sizeof(sys_phy_port_info_t));
    if (NULL == p_usw_phy_master[lchip]->phy_port_info)
    {
        SYS_PHY_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No memory for phy_port_info \n");
        ret = CTC_E_NO_MEMORY;
        goto rollback_1;
    }
    sal_memset(p_usw_phy_master[lchip]->phy_port_info, 0, SYS_PHY_PORT_NUM_PER_SLICE * sizeof(sys_phy_port_info_t));

    p_usw_phy_master[lchip]->port_mdio_mapping_tbl = (uint8 *)mem_malloc(MEM_PORT_MODULE, SYS_PHY_PORT_NUM_PER_SLICE * sizeof(uint8));
    if (NULL == p_usw_phy_master[lchip]->port_mdio_mapping_tbl)
    {
        SYS_PERI_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No memory \n");
        ret = CTC_E_NO_MEMORY;
        goto rollback_1;
    }
    sal_memset(p_usw_phy_master[lchip]->port_mdio_mapping_tbl, 0xFF, SYS_PHY_PORT_NUM_PER_SLICE * sizeof(uint8));

    p_usw_phy_master[lchip]->port_phy_mapping_tbl = (uint8 *)mem_malloc(MEM_PORT_MODULE, SYS_PHY_PORT_NUM_PER_SLICE * sizeof(uint8));
    if (NULL == p_usw_phy_master[lchip]->port_phy_mapping_tbl)
    {
        SYS_PERI_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No memory \n");
        ret = CTC_E_NO_MEMORY;
        goto rollback_1;
    }
    sal_memset(p_usw_phy_master[lchip]->port_phy_mapping_tbl, 0xFF, SYS_PHY_PORT_NUM_PER_SLICE * sizeof(uint8));

    p_usw_phy_master[lchip]->port_dev_no_mapping_tbl = (uint8 *)mem_malloc(MEM_PORT_MODULE, SYS_PHY_PORT_NUM_PER_SLICE * sizeof(uint8));
    if (NULL == p_usw_phy_master[lchip]->port_dev_no_mapping_tbl)
    {
        SYS_PERI_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No memory \n");
        ret = CTC_E_NO_MEMORY;
        goto rollback_1;
    }
    sal_memset(p_usw_phy_master[lchip]->port_dev_no_mapping_tbl, 0xFF, SYS_PHY_PORT_NUM_PER_SLICE * sizeof(uint8));

    return ret;

rollback_1:
    if (p_usw_phy_master[lchip]->port_dev_no_mapping_tbl)
    {
        mem_free(p_usw_phy_master[lchip]->port_dev_no_mapping_tbl);
    }
    if (p_usw_phy_master[lchip]->port_phy_mapping_tbl)
    {
        mem_free(p_usw_phy_master[lchip]->port_phy_mapping_tbl);
    }
    if (p_usw_phy_master[lchip]->port_mdio_mapping_tbl)
    {
        mem_free(p_usw_phy_master[lchip]->port_mdio_mapping_tbl);
    }
    if (p_usw_phy_master[lchip]->phy_port_info)
    {
        mem_free(p_usw_phy_master[lchip]->phy_port_info);
    }
    ctc_slist_delete(p_usw_phy_master[lchip]->phy_list);
rollback_0:
    mem_free(p_usw_phy_master[lchip]);
    p_usw_phy_master[lchip] = 0;

    return ret;

}

int32
sys_usw_phy_deinit(uint8 lchip)
{
    ctc_slistnode_t* node = NULL;
    ctc_slistnode_t* next_node = NULL;

    LCHIP_CHECK(lchip);
    if (NULL == p_usw_phy_master[lchip])
    {
        SYS_PHY_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " phy module not init\n");
		return CTC_E_NONE;
    }

    if (SYS_VCHIP_PP_IS_NOT_BASE(lchip))
    {
        p_usw_phy_master[lchip] = NULL;
        return CTC_E_NONE;
    }

    mem_free(p_usw_phy_master[lchip]->phy_port_info);
    p_usw_phy_master[lchip]->phy_port_info = NULL;

    /*free phy slist*/
    CTC_SLIST_LOOP_DEL(p_usw_phy_master[lchip]->phy_list, node, next_node)
    {
        ctc_slist_delete_node(p_usw_phy_master[lchip]->phy_list, node);
        mem_free(node);
    }
    ctc_slist_delete(p_usw_phy_master[lchip]->phy_list);
    
    mem_free(p_usw_phy_master[lchip]->port_dev_no_mapping_tbl);
    mem_free(p_usw_phy_master[lchip]->port_phy_mapping_tbl);
    mem_free(p_usw_phy_master[lchip]->port_mdio_mapping_tbl);
    mem_free(p_usw_phy_master[lchip]);
    p_usw_phy_master[lchip] = NULL;

    return CTC_E_NONE;
}
